Introduction

In this workshop we will be analysing 5 example samples from a previous batch of samples.

The morphological identification of these samples recorded the following species:

Species Trap1 Trap6 Trap7 Trap19 Trap20
B. aeroginosa 0 0 0 1 0
B. alyxiae 6 1 15 13 0
B. breviaculeus 9 5 18 8 0
B. bryoniae 1 65 35 15 7
B. erubescentis 52 0 0 0 0
B. frauenfeldi 158 164 192 783 0
B. neohumeralis 17 34 20 148 30
B. peninsularis 2 1 1 0 0
B. tenuifascia 1 0 0 0 0
B. tryoni 12 36 31 94 826
D. axanus 3 0 0 0 0
Z. choristus 0 0 0 1 0
Z. strigifinis 1 5 2 0 0

And visually, the trap compositions identified through morphology look like this:

Set up for analysis

Install and load R packages and setup directories

The first step is to install and load all necessary R packages required for the pipeline, as well as the FASTQC and BLAST+ command line software.

#Set required packages
.cran_packages <- c(
  "devtools",
  "ggplot2",
  "gridExtra",
  "data.table",
  "tidyverse", 
  "stringdist",
  "patchwork",
  "vegan",
  "seqinr",
  "patchwork",
  "stringi",
  "magrittr",
  "targets",
  "tarchetypes",
  "zen4R",
  "fs"
  )

.bioc_packages <- c(
  "phyloseq",
  "DECIPHER",
  "Biostrings",
  "ShortRead",
  "ggtree",
  "savR",
  "dada2",
  "ngsReports"
  )

.inst <- .cran_packages %in% installed.packages()
if(any(!.inst)) {
   install.packages(.cran_packages[!.inst])
}
.inst <- .bioc_packages %in% installed.packages()
if(any(!.inst)) {
  if (!requireNamespace("BiocManager", quietly = TRUE)){
    install.packages("BiocManager")
  }
  BiocManager::install(.bioc_packages[!.inst], ask = F)
}

#Load all published packages
sapply(c(.cran_packages,.bioc_packages), require, character.only = TRUE)

# Install and load github packages
devtools::install_github("alexpiper/seqateurs", dependencies = TRUE)
library(seqateurs)

devtools::install_github("alexpiper/taxreturn", dependencies = TRUE)
library(taxreturn)

devtools::install_github("alexpiper/afdscraper", dependencies = TRUE)
library(afdscraper)

devtools::install_github("mikemc/speedyseq", dependencies = TRUE)
library(speedyseq)

#Install bbmap if its not in $path or in bin folder
if(Sys.which("bbduk") == "" & !file.exists("bin/bbmap/bbduk.sh")){
  seqateurs::bbmap_install(dest_dir = "bin")
}

#Install fastqc if its not in $path or in bin folder
if(Sys.which("fastqc") == "" & !file.exists("bin/FastQC/fastqc")){
  seqateurs::fastqc_install(dest_dir = "bin")
}

#Install BLAST if its not in $path or in bin folder
if(Sys.which("blastn") == "" & (length(fs::dir_ls("bin", glob="*blastn.exe",recurse = TRUE)) ==0)){
  taxreturn::blast_install(dest_dir = "bin")
}

source("R/dependencies.R")
source("R/functions.R")
source("R/themes.R")

Create directory structure

This step creates the required directory structure for the pipeline to run.

# Create directories
if(!dir.exists("data")){dir.create("data", recursive = TRUE)}
if(!dir.exists("reference")){dir.create("reference", recursive = TRUE)}
if(!dir.exists("output/logs")){dir.create("output/logs", recursive = TRUE)}
if(!dir.exists("output/results")){dir.create("output/results", recursive = TRUE)}
if(!dir.exists("output/rds")){dir.create("output/rds", recursive = TRUE)}
if(!dir.exists("sample_data")){dir.create("sample_data", recursive = TRUE)}
if(!dir.exists("output/results/final")) {dir.create("output/results/final", recursive = TRUE)}
if(!dir.exists("output/results/unfiltered")) {dir.create("output/results/unfiltered", recursive = TRUE)}
if(!dir.exists("output/results/filtered")) {dir.create("output/results/filtered", recursive = TRUE)}

Fetch sequencing reads

Some test sequencing reads have been hosted on Zenodo for this workshop. The below code will download these and put them inside the data folder.

# Create directory for data
if(!dir.exists("data/K77JP")) {dir.create("data/K77JP", recursive = TRUE)}
if(!dir.exists("data/K77JP/InterOp")) {dir.create("data/K77JP/InterOp", recursive = TRUE)}

# Download files from zenodo
download_zenodo(
doi = "10.5281/zenodo.7112162",
path = "data/K77JP"
)

# Move the interop files to the interop folder
fs::dir_ls(path="data/K77JP", glob="*.bin") %>%
  purrr::map(function(x){
    fs::file_copy(path = x, new_path = x %>% str_replace("data/K77JP", "data/K77JP/InterOp"))
    file.remove(x)
  })

The directory structure should now look something like this:

root/
├── data/
│   ├── K77JP/
│     ├── R1.fastq.gz
│     ├── R2.fastq.gz
│     ├── runInfo.xml
│     ├── runParameters.xml
│     ├── SampleSheet.csv
│     └── InterOp/
├── sample_data/
├── reference
├── bin
├── output/
└── doc/

Create sample sheet

In order to track samples and relevant QC statistics throughout the metabarcoding pipeline, we will first create a new samplesheet from our input samplesheets. This function requires both the SampleSheet.csv used for the sequencing run, and the runParameters.xml, both of which should have been automatically obtained from the demultiplexed sequencing run folder in the bash step above

# Find all flowcell subdirectories within the data directory
runs <- dir("data/")
SampleSheet <- list.files(paste0("data/", runs), pattern= "SampleSheet", full.names = TRUE)
runParameters <- list.files(paste0("data/", runs), pattern= "[Rr]unParameters.xml", full.names = TRUE)

# Create samplesheet containing samples and run parameters for all runs
samdf <- create_samplesheet(SampleSheet = SampleSheet, runParameters = runParameters, template = "V4") %>%
  distinct()

# Check if sampleids contain fcid, if not, attatch these
samdf <- samdf %>%
  mutate(sample_id = case_when(
    !str_detect(sample_id, fcid) ~ paste0(fcid,"_",sample_id),
    TRUE ~ sample_id
  ))

# Get a list of the fastq files
fastqFs <- purrr::map(list.dirs("data", recursive=FALSE),
                      list.files, pattern="_R1_", full.names = TRUE) %>%
  unlist() %>%
  str_remove(pattern = "^(.*)\\/") %>%
  str_remove(pattern = "(?:.(?!_S))+$")
fastqFs <- fastqFs[!str_detect(fastqFs, "Undetermined")]

# Find those that are missing in th sample sheet
if (length(setdiff(fastqFs, samdf$sample_id)) > 0) {warning("The fastq file/s: ", setdiff(fastqFs, samdf$sample_id), " are not in the sample sheet") }

# Find those samples in the samplesheet that don't have fastq files
if (length(setdiff(samdf$sample_id, fastqFs)) > 0) {
  warning(paste0("The fastq file: ",
                 setdiff(samdf$sample_id, fastqFs),
                 " is missing, dropping from samplesheet \n")) 
  samdf <- samdf %>%
    filter(!sample_id %in% setdiff(samdf$sample_id, fastqFs))
}

# Add PCR primers to sample sheet
samdf <- samdf %>%
  mutate(
    pcr_primers = "fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4",
    for_primer_seq = "GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC",
    rev_primer_seq = "GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC"
    )

#Write out sample CSV for use in pipeline
dir.create("sample_data")
write_csv(samdf, "sample_data/Sample_info.csv")
The resulting sample data file should look like this:
sample_id sample_name extraction_rep amp_rep client_name experiment_name sample_type collection_method collection_location lat_lon environment collection_date operator_name description assay extraction_method amp_method target_gene pcr_primers for_primer_seq rev_primer_seq index_plate index_well i7_index_id i7_index i5_index_id i5_index seq_platform fcid for_read_length rev_read_length seq_run_id seq_id seq_date analysis_method notes
K77JP_Trap7 Trap7 NA NA Pathogens K739J_tephritid_metabarcoding NA NA NA NA NA NA Alexander Piper NA Metabarcoding NA NA NA fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4 GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC 1 B1 AVR_DUI_i7_002 ACGGAACA AVR_DUI_i5_002 CGCTCTAT NA K77JP 251 251 220527_M01054_0780_000000000-K77JP M01054 2022-05-27 NA NA
K77JP_Trap6 Trap6 NA NA Pathogens K739J_tephritid_metabarcoding NA NA NA NA NA NA Alexander Piper NA Metabarcoding NA NA NA fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4 GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC 1 C1 AVR_DUI_i7_003 CTTAGGAC AVR_DUI_i5_003 TGGTAGCT NA K77JP 251 251 220527_M01054_0780_000000000-K77JP M01054 2022-05-27 NA NA
K77JP_Trap1 Trap1 NA NA Pathogens K739J_tephritid_metabarcoding NA NA NA NA NA NA Alexander Piper NA Metabarcoding NA NA NA fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4 GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC 1 H1 AVR_DUI_i7_008 ACTCGTTG AVR_DUI_i5_008 CACCACTA NA K77JP 251 251 220527_M01054_0780_000000000-K77JP M01054 2022-05-27 NA NA
K77JP_Trap20 Trap20 NA NA Pathogens K739J_tephritid_metabarcoding NA NA NA NA NA NA Alexander Piper NA Metabarcoding NA NA NA fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4 GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC 1 E3 AVR_DUI_i7_021 GCTGACTA AVR_DUI_i5_021 CTTAGGAC NA K77JP 251 251 220527_M01054_0780_000000000-K77JP M01054 2022-05-27 NA NA
K77JP_Trap19 Trap19 NA NA Pathogens K739J_tephritid_metabarcoding NA NA NA NA NA NA Alexander Piper NA Metabarcoding NA NA NA fwhF2-fwhR2nDac;EIF3LminiF4-EIF3lminiR4 GGDACWGGWTGAACWGTWTAYCCHCC;GATGCGYCGTTATGCYGATGC GTRATWGCHCCIGCTAADACHGG;TTRAAYACTTCYARATCRCC 1 F3 AVR_DUI_i7_022 CAGGAGAT AVR_DUI_i5_022 ACGGAACA NA K77JP 251 251 220527_M01054_0780_000000000-K77JP M01054 2022-05-27 NA NA

Create parameters file

The pipeline also requires a locus parameters file which lists important information about the target barcodes, as well as the PHMM model used to clean the loci, as well as the different reference databases that are to be used for taxonomic assignment.

If multiple reference databases are to be used iteratively for assignment, they should be split with a semicolon, and in the order they are to be used for assignment.

The below code generates the loci_params.csv file, however this can also be done in excel.

params <- tibble(
  pcr_primers = c("fwhF2-fwhR2nDac", "EIF3LminiF4-EIF3lminiR4"),
  target_gene=c("COI", "EIF3L"),
  phmm = c("reference/phmm/Bactrocera_COI.rds", "reference/phmm/Bactrocera_EIF3L.rds"),
  ref_db = c("reference/COI_internal_idtaxa.rds;reference/COI_idtaxa.rds","reference/EIF3L_internal_idtaxa.rds;reference/EIF3L_idtaxa.rds"),
  blast_db = c("reference/COI_internal.fa.gz;reference/COI_hierarchial.fa.gz", "reference/EIF3L_internal.fa.gz;reference/EIF3L_hierarchial.fa.gz"),
  exp_length = c(205, 217),
  genetic_code = c("SGC4", "SGC0"),
  coding = c(TRUE, TRUE)
)

# Write out the parameters file
write_csv(params, "sample_data/loci_params.csv")
The resulting table should look like this:
pcr_primers target_gene phmm ref_db blast_db exp_length genetic_code coding
fwhF2-fwhR2nDac COI reference/phmm/Bactrocera_COI.rds reference/COI_internal_idtaxa.rds;reference/COI_idtaxa.rds reference/COI_internal.fa.gz;reference/COI_hierarchial.fa.gz 205 SGC4 TRUE
EIF3LminiF4-EIF3lminiR4 EIF3L reference/phmm/Bactrocera_EIF3L.rds reference/EIF3L_internal_idtaxa.rds;reference/EIF3L_idtaxa.rds reference/EIF3L_internal.fa.gz;reference/EIF3L_hierarchial.fa.gz 217 SGC0 TRUE

Run the pipeline

Now that the sample data and parameters file is ready, its time to run the pipeline!

Visualise the pipeline steps

# Visualise the planned targets pipeline
tar_glimpse()
## There were 38 warnings (use warnings() to see them)

Run the targets pipeline

tar_make()

Check quality control outputs

There are a few important quality control plots that have been automatically created throughout the pipeline. These should be checked to ensure the sequencing run and analysis has run successfully:

  • Sequencing run quality check
    • output/logs/K77JP/PFclusters.pdf
    • output/logs/K77JP/Qscore_L1.pdf
    • output/logs/K77JP/avg_intensity.pdf
  • Sample quality check
    • output/logs/K77JP/FASTQC/ngsReports_Fastqc.html
  • Index-switching
    • output/logs/K77JP/index_switching.pdf
  • Filtering plots
    • output/logs/K77JP/prefilt_qualplots.pdf
    • output/logs/K77JP/postfilt_qualplots.pdf
  • DADA2 error model
    • output/logs/K77JP/fcid_errormodel.pdf
  • Reads surviving pipeline
    • output/logs/K77JP/read_survival.pdf NOT WORKING.

Analyse the results

The outputs of the pipeline are an ASV table, taxonomy table, and sample data, as well as a phyloseq object containing these 3 components. these are located in the following directories

  • output/results/final/seqtab.csv
  • output/results/final/taxtab.csv
  • output/results/final/samdf.csv
  • output/rds/ps_filtered.rds

Read in data

Here we read in the phyloseq object containing the ASV table, taxonomy table, and sample data

ps <- readRDS("output/rds/ps_filtered.rds")

# Turn the phyloseq object into an R data frame, and apply 0.01% minimum abundance threshold
summary_dat <- ps %>%
  speedyseq::tax_glom(taxrank = "Species") %>% # Merges all ASVs assigned to the same species
  speedyseq::psmelt()  %>% # Transforms to data frame
  filter(Abundance > 0 )  %>%
  dplyr::select(OTU, Sample, Abundance,pcr_primers, sample_id, sample_name, environment, collection_location, collection_date, fcid, rank_names(ps)) %>%
  group_by(sample_id, pcr_primers) %>%
  mutate_at(vars(Abundance), ~ . / sum(.) ) %>%
  filter(Abundance > 1e-4) # Remove all under 0.01% abundance

Heatmaps

gg.heatmap <- summary_dat%>%
  mutate(sample_name = factor(sample_name,levels=c("Trap1", "Trap6", "Trap7", "Trap19", "Trap20"))) %>%
  ggplot(aes(x=sample_name, y=Species, fill=Abundance)) +
    geom_tile() +
    scale_fill_viridis_c(labels = scales::percent, na.value = NA, alpha=0.9) +
    scale_y_discrete(limits=rev)+
    base_theme+
    theme(legend.position = "right",
          axis.text.y = element_text(face="italic"),
          axis.title.y = element_blank())+
      labs(x="Sample",
           y="Taxon",
           fill="Relative abundance") +
    facet_grid(~pcr_primers, drop=TRUE)

ggplotly(gg.heatmap)

Barplots

gg.barplot <- summary_dat%>%
  mutate(sample_name = factor(sample_name,levels=c("Trap1", "Trap6", "Trap7", "Trap19", "Trap20"))) %>%
  ggplot(aes(x=sample_name, y=Abundance, fill=Species)) +
    geom_col(colour="black") +
    #scale_fill_viridis_c(labels = scales::percent, na.value = NA, alpha=0.9) +
    #scale_y_discrete(limits=rev)+
    base_theme+
    theme(legend.position = "none",
          axis.text.y = element_text(face="italic"),
          axis.title.y = element_blank())+
      labs(x="Sample",
           y="Taxon",
           fill="Relative abundance") +
    facet_grid(~pcr_primers, drop=TRUE)

ggplotly(gg.barplot)
LS0tDQp0aXRsZTogIlRlcGhyaXRpZCBtZXRhYmFyY29kaW5nIHdvcmtzaG9wIg0Kc3VidGl0bGU6ICJCaW9pbmZvcm1hdGljIGFuYWx5c2lzIg0KYXV0aG9yOiAiQWxleGFuZGVyIE0uIFBpcGVyIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBoaWdobGlnaHRlcjogbnVsbA0KICAgIHRoZW1lOiAiZmxhdGx5Ig0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IA0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZCAgICANCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIEtuaXRyIGdsb2JhbCBzZXR1cCAtIGNoYW5nZSBldmFsIHRvIHRydWUgdG8gcnVuIGNvZGUNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeSh0YXJnZXRzKQ0KbGlicmFyeSh0YXJjaGV0eXBlcykNCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgZmlnLnNob3cgPSAiaG9sZCIsIGZpZy5rZWVwID0gImFsbCIpDQpvcHRzX2NodW5rJHNldChkZXYgPSAncG5nJykNCnNvdXJjZSgiUi90aGVtZXMuUiIpDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KSW4gdGhpcyB3b3Jrc2hvcCB3ZSB3aWxsIGJlIGFuYWx5c2luZyA1IGV4YW1wbGUgc2FtcGxlcyBmcm9tIGEgcHJldmlvdXMgYmF0Y2ggb2Ygc2FtcGxlcy4gDQoNClRoZSBtb3JwaG9sb2dpY2FsIGlkZW50aWZpY2F0aW9uIG9mIHRoZXNlIHNhbXBsZXMgcmVjb3JkZWQgdGhlIGZvbGxvd2luZyBzcGVjaWVzOg0KDQpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQ0KdHJhcF9jYXRjaGVzIDwtIHRpYmJsZTo6dHJpYmJsZSgNCiAgflNwZWNpZXMsIH5UcmFwMSwgflRyYXA2LCB+VHJhcDcsIH5UcmFwMTksIH5UcmFwMjAsDQogIkIuIGFlcm9naW5vc2EiLCAwLDAsMCwxLDAsDQogIkIuIGFseXhpYWUiLCA2LDEsMTUsMTMsMCwNCiAiQi4gYnJldmlhY3VsZXVzIiw5LDUsMTgsOCwwLA0KICJCLiBicnlvbmlhZSIsMSw2NSwzNSwxNSw3LA0KICJCLiBlcnViZXNjZW50aXMiLDUyLDAsMCwwLDAsDQogIkIuIGZyYXVlbmZlbGRpIiwxNTgsMTY0LDE5Miw3ODMsMCwNCiAiQi4gbmVvaHVtZXJhbGlzIiwxNywzNCwyMCwxNDgsMzAsDQogIkIuIHBlbmluc3VsYXJpcyIsMiwxLDEsMCwwLA0KICJCLiB0ZW51aWZhc2NpYSIsMSwwLDAsMCwwLA0KICJCLiB0cnlvbmkiLDEyLDM2LDMxLDk0LDgyNiwNCiAiRC4gYXhhbnVzIiwzLDAsMCwwLDAsDQogIlouIGNob3Jpc3R1cyIsMCwwLDAsMSwwLA0KICJaLiBzdHJpZ2lmaW5pcyIsMSw1LDIsMCwwDQopDQp0cmFwX2NhdGNoZXMlPiUNCiAga2JsKCkgJT4lDQogIGthYmxlX2NsYXNzaWMoImhvdmVyIiwgZnVsbF93aWR0aCA9IFQpDQpgYGANCg0KQW5kIHZpc3VhbGx5LCB0aGUgdHJhcCBjb21wb3NpdGlvbnMgaWRlbnRpZmllZCB0aHJvdWdoIG1vcnBob2xvZ3kgbG9vayBsaWtlIHRoaXM6DQoNCmBgYHtyLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9DQpwbG90IDwtIHRyYXBfY2F0Y2hlcyAlPiUNCiAgcGl2b3RfbG9uZ2VyKC1TcGVjaWVzLA0KICAgICAgICAgICAgICAgbmFtZXNfdG89InNhbXBsZSIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG89InNwZWNpbWVucyIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBzYW1wbGUsIHkgPSBzcGVjaW1lbnMsIGZpbGw9U3BlY2llcykpICsgDQogIGdlb21fY29sKCkgKw0KICAjc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpKw0KICBiYXNlX3RoZW1lICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKw0KICBsYWJzKHggPSAiVHJhcCBzYW1wbGUiLA0KICAgICAgIHkgPSAiTnVtYmVyIG9mIG1vcnBob2xvZ2ljYWxseSBpZGVudGlmaWVkIHNwZWNpbWVucyIpIA0KDQpnZ3Bsb3RseShwbG90KQ0KYGBgDQoNCiMgU2V0IHVwIGZvciBhbmFseXNpcw0KDQojIyBJbnN0YWxsIGFuZCBsb2FkIFIgcGFja2FnZXMgYW5kIHNldHVwIGRpcmVjdG9yaWVzDQoNClRoZSBmaXJzdCBzdGVwIGlzIHRvIGluc3RhbGwgYW5kIGxvYWQgYWxsIG5lY2Vzc2FyeSBSIHBhY2thZ2VzIHJlcXVpcmVkIGZvciB0aGUgcGlwZWxpbmUsIGFzIHdlbGwgYXMgdGhlIEZBU1RRQyBhbmQgQkxBU1QrIGNvbW1hbmQgbGluZSBzb2Z0d2FyZS4NCg0KYGBge3IgaW5zdGFsbCBhbmQgbG9hZCwgZXZhbD1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgcmVzdWx0cz1GQUxTRX0gDQojU2V0IHJlcXVpcmVkIHBhY2thZ2VzDQouY3Jhbl9wYWNrYWdlcyA8LSBjKA0KICAiZGV2dG9vbHMiLA0KICAiZ2dwbG90MiIsDQogICJncmlkRXh0cmEiLA0KICAiZGF0YS50YWJsZSIsDQogICJ0aWR5dmVyc2UiLCANCiAgInN0cmluZ2Rpc3QiLA0KICAicGF0Y2h3b3JrIiwNCiAgInZlZ2FuIiwNCiAgInNlcWluciIsDQogICJwYXRjaHdvcmsiLA0KICAic3RyaW5naSIsDQogICJtYWdyaXR0ciIsDQogICJ0YXJnZXRzIiwNCiAgInRhcmNoZXR5cGVzIiwNCiAgInplbjRSIiwNCiAgImZzIg0KICApDQoNCi5iaW9jX3BhY2thZ2VzIDwtIGMoDQogICJwaHlsb3NlcSIsDQogICJERUNJUEhFUiIsDQogICJCaW9zdHJpbmdzIiwNCiAgIlNob3J0UmVhZCIsDQogICJnZ3RyZWUiLA0KICAic2F2UiIsDQogICJkYWRhMiIsDQogICJuZ3NSZXBvcnRzIg0KICApDQoNCi5pbnN0IDwtIC5jcmFuX3BhY2thZ2VzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkNCmlmKGFueSghLmluc3QpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKC5jcmFuX3BhY2thZ2VzWyEuaW5zdF0pDQp9DQouaW5zdCA8LSAuYmlvY19wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpDQppZihhbnkoIS5pbnN0KSkgew0KICBpZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpDQogIH0NCiAgQmlvY01hbmFnZXI6Omluc3RhbGwoLmJpb2NfcGFja2FnZXNbIS5pbnN0XSwgYXNrID0gRikNCn0NCg0KI0xvYWQgYWxsIHB1Ymxpc2hlZCBwYWNrYWdlcw0Kc2FwcGx5KGMoLmNyYW5fcGFja2FnZXMsLmJpb2NfcGFja2FnZXMpLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQoNCiMgSW5zdGFsbCBhbmQgbG9hZCBnaXRodWIgcGFja2FnZXMNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWxleHBpcGVyL3NlcWF0ZXVycyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQpsaWJyYXJ5KHNlcWF0ZXVycykNCg0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJhbGV4cGlwZXIvdGF4cmV0dXJuIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCmxpYnJhcnkodGF4cmV0dXJuKQ0KDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImFsZXhwaXBlci9hZmRzY3JhcGVyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCmxpYnJhcnkoYWZkc2NyYXBlcikNCg0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJtaWtlbWMvc3BlZWR5c2VxIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCmxpYnJhcnkoc3BlZWR5c2VxKQ0KDQojSW5zdGFsbCBiYm1hcCBpZiBpdHMgbm90IGluICRwYXRoIG9yIGluIGJpbiBmb2xkZXINCmlmKFN5cy53aGljaCgiYmJkdWsiKSA9PSAiIiAmICFmaWxlLmV4aXN0cygiYmluL2JibWFwL2JiZHVrLnNoIikpew0KICBzZXFhdGV1cnM6OmJibWFwX2luc3RhbGwoZGVzdF9kaXIgPSAiYmluIikNCn0NCg0KI0luc3RhbGwgZmFzdHFjIGlmIGl0cyBub3QgaW4gJHBhdGggb3IgaW4gYmluIGZvbGRlcg0KaWYoU3lzLndoaWNoKCJmYXN0cWMiKSA9PSAiIiAmICFmaWxlLmV4aXN0cygiYmluL0Zhc3RRQy9mYXN0cWMiKSl7DQogIHNlcWF0ZXVyczo6ZmFzdHFjX2luc3RhbGwoZGVzdF9kaXIgPSAiYmluIikNCn0NCg0KI0luc3RhbGwgQkxBU1QgaWYgaXRzIG5vdCBpbiAkcGF0aCBvciBpbiBiaW4gZm9sZGVyDQppZihTeXMud2hpY2goImJsYXN0biIpID09ICIiICYgKGxlbmd0aChmczo6ZGlyX2xzKCJiaW4iLCBnbG9iPSIqYmxhc3RuLmV4ZSIscmVjdXJzZSA9IFRSVUUpKSA9PTApKXsNCiAgdGF4cmV0dXJuOjpibGFzdF9pbnN0YWxsKGRlc3RfZGlyID0gImJpbiIpDQp9DQoNCnNvdXJjZSgiUi9kZXBlbmRlbmNpZXMuUiIpDQpzb3VyY2UoIlIvZnVuY3Rpb25zLlIiKQ0Kc291cmNlKCJSL3RoZW1lcy5SIikNCmBgYA0KDQojIyBDcmVhdGUgZGlyZWN0b3J5IHN0cnVjdHVyZQ0KDQpUaGlzIHN0ZXAgY3JlYXRlcyB0aGUgcmVxdWlyZWQgZGlyZWN0b3J5IHN0cnVjdHVyZSBmb3IgdGhlIHBpcGVsaW5lIHRvIHJ1bi4gDQoNCmBgYHtyfQ0KIyBDcmVhdGUgZGlyZWN0b3JpZXMNCmlmKCFkaXIuZXhpc3RzKCJkYXRhIikpe2Rpci5jcmVhdGUoImRhdGEiLCByZWN1cnNpdmUgPSBUUlVFKX0NCmlmKCFkaXIuZXhpc3RzKCJyZWZlcmVuY2UiKSl7ZGlyLmNyZWF0ZSgicmVmZXJlbmNlIiwgcmVjdXJzaXZlID0gVFJVRSl9DQppZighZGlyLmV4aXN0cygib3V0cHV0L2xvZ3MiKSl7ZGlyLmNyZWF0ZSgib3V0cHV0L2xvZ3MiLCByZWN1cnNpdmUgPSBUUlVFKX0NCmlmKCFkaXIuZXhpc3RzKCJvdXRwdXQvcmVzdWx0cyIpKXtkaXIuY3JlYXRlKCJvdXRwdXQvcmVzdWx0cyIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KaWYoIWRpci5leGlzdHMoIm91dHB1dC9yZHMiKSl7ZGlyLmNyZWF0ZSgib3V0cHV0L3JkcyIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KaWYoIWRpci5leGlzdHMoInNhbXBsZV9kYXRhIikpe2Rpci5jcmVhdGUoInNhbXBsZV9kYXRhIiwgcmVjdXJzaXZlID0gVFJVRSl9DQppZighZGlyLmV4aXN0cygib3V0cHV0L3Jlc3VsdHMvZmluYWwiKSkge2Rpci5jcmVhdGUoIm91dHB1dC9yZXN1bHRzL2ZpbmFsIiwgcmVjdXJzaXZlID0gVFJVRSl9DQppZighZGlyLmV4aXN0cygib3V0cHV0L3Jlc3VsdHMvdW5maWx0ZXJlZCIpKSB7ZGlyLmNyZWF0ZSgib3V0cHV0L3Jlc3VsdHMvdW5maWx0ZXJlZCIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KaWYoIWRpci5leGlzdHMoIm91dHB1dC9yZXN1bHRzL2ZpbHRlcmVkIikpIHtkaXIuY3JlYXRlKCJvdXRwdXQvcmVzdWx0cy9maWx0ZXJlZCIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KYGBgDQoNCiMjIEZldGNoIHNlcXVlbmNpbmcgcmVhZHMNCg0KU29tZSB0ZXN0IHNlcXVlbmNpbmcgcmVhZHMgaGF2ZSBiZWVuIGhvc3RlZCBvbiBaZW5vZG8gZm9yIHRoaXMgd29ya3Nob3AuIFRoZSBiZWxvdyBjb2RlIHdpbGwgZG93bmxvYWQgdGhlc2UgYW5kIHB1dCB0aGVtIGluc2lkZSB0aGUgZGF0YSBmb2xkZXIuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgZGlyZWN0b3J5IGZvciBkYXRhDQppZighZGlyLmV4aXN0cygiZGF0YS9LNzdKUCIpKSB7ZGlyLmNyZWF0ZSgiZGF0YS9LNzdKUCIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQ0KaWYoIWRpci5leGlzdHMoImRhdGEvSzc3SlAvSW50ZXJPcCIpKSB7ZGlyLmNyZWF0ZSgiZGF0YS9LNzdKUC9JbnRlck9wIiwgcmVjdXJzaXZlID0gVFJVRSl9DQoNCiMgRG93bmxvYWQgZmlsZXMgZnJvbSB6ZW5vZG8NCmRvd25sb2FkX3plbm9kbygNCmRvaSA9ICIxMC41MjgxL3plbm9kby43MTEyMTYyIiwNCnBhdGggPSAiZGF0YS9LNzdKUCINCikNCg0KIyBNb3ZlIHRoZSBpbnRlcm9wIGZpbGVzIHRvIHRoZSBpbnRlcm9wIGZvbGRlcg0KZnM6OmRpcl9scyhwYXRoPSJkYXRhL0s3N0pQIiwgZ2xvYj0iKi5iaW4iKSAlPiUNCiAgcHVycnI6Om1hcChmdW5jdGlvbih4KXsNCiAgICBmczo6ZmlsZV9jb3B5KHBhdGggPSB4LCBuZXdfcGF0aCA9IHggJT4lIHN0cl9yZXBsYWNlKCJkYXRhL0s3N0pQIiwgImRhdGEvSzc3SlAvSW50ZXJPcCIpKQ0KICAgIGZpbGUucmVtb3ZlKHgpDQogIH0pDQpgYGANCg0KVGhlIGRpcmVjdG9yeSBzdHJ1Y3R1cmUgc2hvdWxkIG5vdyBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6DQoNCiAgICByb290Lw0KICAgIOKUnOKUgOKUgCBkYXRhLw0KICAgIOKUgiAgIOKUnOKUgOKUgCBLNzdKUC8NCiAgICDilIIgICAgIOKUnOKUgOKUgCBSMS5mYXN0cS5neg0KICAgIOKUgiAgICAg4pSc4pSA4pSAIFIyLmZhc3RxLmd6DQogICAg4pSCICAgICDilJzilIDilIAgcnVuSW5mby54bWwNCiAgICDilIIgICAgIOKUnOKUgOKUgCBydW5QYXJhbWV0ZXJzLnhtbA0KICAgIOKUgiAgICAg4pSc4pSA4pSAIFNhbXBsZVNoZWV0LmNzdg0KICAgIOKUgiAgICAg4pSU4pSA4pSAIEludGVyT3AvDQogICAg4pSc4pSA4pSAIHNhbXBsZV9kYXRhLw0KICAgIOKUnOKUgOKUgCByZWZlcmVuY2UNCiAgICDilJzilIDilIAgYmluDQogICAg4pSc4pSA4pSAIG91dHB1dC8NCiAgICDilJTilIDilIAgZG9jLw0KDQoNCiMjIENyZWF0ZSBzYW1wbGUgc2hlZXQgDQoNCkluIG9yZGVyIHRvIHRyYWNrIHNhbXBsZXMgYW5kIHJlbGV2YW50IFFDIHN0YXRpc3RpY3MgdGhyb3VnaG91dCB0aGUgbWV0YWJhcmNvZGluZyBwaXBlbGluZSwgd2Ugd2lsbCBmaXJzdCBjcmVhdGUgYSBuZXcgc2FtcGxlc2hlZXQgZnJvbSBvdXIgaW5wdXQgc2FtcGxlc2hlZXRzLiBUaGlzIGZ1bmN0aW9uIHJlcXVpcmVzIGJvdGggdGhlIFNhbXBsZVNoZWV0LmNzdiB1c2VkIGZvciB0aGUgc2VxdWVuY2luZyBydW4sIGFuZCB0aGUgcnVuUGFyYW1ldGVycy54bWwsIGJvdGggb2Ygd2hpY2ggc2hvdWxkIGhhdmUgYmVlbiBhdXRvbWF0aWNhbGx5IG9idGFpbmVkIGZyb20gdGhlIGRlbXVsdGlwbGV4ZWQgc2VxdWVuY2luZyBydW4gZm9sZGVyIGluIHRoZSBiYXNoIHN0ZXAgYWJvdmUNCg0KYGBge3IgY3JlYXRlIHNhbXBsZXNoZWV0LCBldmFsPVRSVUV9DQojIEZpbmQgYWxsIGZsb3djZWxsIHN1YmRpcmVjdG9yaWVzIHdpdGhpbiB0aGUgZGF0YSBkaXJlY3RvcnkNCnJ1bnMgPC0gZGlyKCJkYXRhLyIpDQpTYW1wbGVTaGVldCA8LSBsaXN0LmZpbGVzKHBhc3RlMCgiZGF0YS8iLCBydW5zKSwgcGF0dGVybj0gIlNhbXBsZVNoZWV0IiwgZnVsbC5uYW1lcyA9IFRSVUUpDQpydW5QYXJhbWV0ZXJzIDwtIGxpc3QuZmlsZXMocGFzdGUwKCJkYXRhLyIsIHJ1bnMpLCBwYXR0ZXJuPSAiW1JyXXVuUGFyYW1ldGVycy54bWwiLCBmdWxsLm5hbWVzID0gVFJVRSkNCg0KIyBDcmVhdGUgc2FtcGxlc2hlZXQgY29udGFpbmluZyBzYW1wbGVzIGFuZCBydW4gcGFyYW1ldGVycyBmb3IgYWxsIHJ1bnMNCnNhbWRmIDwtIGNyZWF0ZV9zYW1wbGVzaGVldChTYW1wbGVTaGVldCA9IFNhbXBsZVNoZWV0LCBydW5QYXJhbWV0ZXJzID0gcnVuUGFyYW1ldGVycywgdGVtcGxhdGUgPSAiVjQiKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojIENoZWNrIGlmIHNhbXBsZWlkcyBjb250YWluIGZjaWQsIGlmIG5vdCwgYXR0YXRjaCB0aGVzZQ0Kc2FtZGYgPC0gc2FtZGYgJT4lDQogIG11dGF0ZShzYW1wbGVfaWQgPSBjYXNlX3doZW4oDQogICAgIXN0cl9kZXRlY3Qoc2FtcGxlX2lkLCBmY2lkKSB+IHBhc3RlMChmY2lkLCJfIixzYW1wbGVfaWQpLA0KICAgIFRSVUUgfiBzYW1wbGVfaWQNCiAgKSkNCg0KIyBHZXQgYSBsaXN0IG9mIHRoZSBmYXN0cSBmaWxlcw0KZmFzdHFGcyA8LSBwdXJycjo6bWFwKGxpc3QuZGlycygiZGF0YSIsIHJlY3Vyc2l2ZT1GQUxTRSksDQogICAgICAgICAgICAgICAgICAgICAgbGlzdC5maWxlcywgcGF0dGVybj0iX1IxXyIsIGZ1bGwubmFtZXMgPSBUUlVFKSAlPiUNCiAgdW5saXN0KCkgJT4lDQogIHN0cl9yZW1vdmUocGF0dGVybiA9ICJeKC4qKVxcLyIpICU+JQ0KICBzdHJfcmVtb3ZlKHBhdHRlcm4gPSAiKD86Lig/IV9TKSkrJCIpDQpmYXN0cUZzIDwtIGZhc3RxRnNbIXN0cl9kZXRlY3QoZmFzdHFGcywgIlVuZGV0ZXJtaW5lZCIpXQ0KDQojIEZpbmQgdGhvc2UgdGhhdCBhcmUgbWlzc2luZyBpbiB0aCBzYW1wbGUgc2hlZXQNCmlmIChsZW5ndGgoc2V0ZGlmZihmYXN0cUZzLCBzYW1kZiRzYW1wbGVfaWQpKSA+IDApIHt3YXJuaW5nKCJUaGUgZmFzdHEgZmlsZS9zOiAiLCBzZXRkaWZmKGZhc3RxRnMsIHNhbWRmJHNhbXBsZV9pZCksICIgYXJlIG5vdCBpbiB0aGUgc2FtcGxlIHNoZWV0IikgfQ0KDQojIEZpbmQgdGhvc2Ugc2FtcGxlcyBpbiB0aGUgc2FtcGxlc2hlZXQgdGhhdCBkb24ndCBoYXZlIGZhc3RxIGZpbGVzDQppZiAobGVuZ3RoKHNldGRpZmYoc2FtZGYkc2FtcGxlX2lkLCBmYXN0cUZzKSkgPiAwKSB7DQogIHdhcm5pbmcocGFzdGUwKCJUaGUgZmFzdHEgZmlsZTogIiwNCiAgICAgICAgICAgICAgICAgc2V0ZGlmZihzYW1kZiRzYW1wbGVfaWQsIGZhc3RxRnMpLA0KICAgICAgICAgICAgICAgICAiIGlzIG1pc3NpbmcsIGRyb3BwaW5nIGZyb20gc2FtcGxlc2hlZXQgXG4iKSkgDQogIHNhbWRmIDwtIHNhbWRmICU+JQ0KICAgIGZpbHRlcighc2FtcGxlX2lkICVpbiUgc2V0ZGlmZihzYW1kZiRzYW1wbGVfaWQsIGZhc3RxRnMpKQ0KfQ0KDQojIEFkZCBQQ1IgcHJpbWVycyB0byBzYW1wbGUgc2hlZXQNCnNhbWRmIDwtIHNhbWRmICU+JQ0KICBtdXRhdGUoDQogICAgcGNyX3ByaW1lcnMgPSAiZndoRjItZndoUjJuRGFjO0VJRjNMbWluaUY0LUVJRjNsbWluaVI0IiwNCiAgICBmb3JfcHJpbWVyX3NlcSA9ICJHR0RBQ1dHR1dUR0FBQ1dHVFdUQVlDQ0hDQztHQVRHQ0dZQ0dUVEFUR0NZR0FUR0MiLA0KICAgIHJldl9wcmltZXJfc2VxID0gIkdUUkFUV0dDSENDSUdDVEFBREFDSEdHO1RUUkFBWUFDVFRDWUFSQVRDUkNDIg0KICAgICkNCg0KI1dyaXRlIG91dCBzYW1wbGUgQ1NWIGZvciB1c2UgaW4gcGlwZWxpbmUNCmRpci5jcmVhdGUoInNhbXBsZV9kYXRhIikNCndyaXRlX2NzdihzYW1kZiwgInNhbXBsZV9kYXRhL1NhbXBsZV9pbmZvLmNzdiIpDQpgYGANCg0KVGhlIHJlc3VsdGluZyBzYW1wbGUgZGF0YSBmaWxlIHNob3VsZCBsb29rIGxpa2UgdGhpczoNCmBgYHtyLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9DQpzYW1kZiAlPiUNCiAgaGVhZCgpJT4lDQogIGthYmxlKCkgJT4lDQogIGthYmxlX2NsYXNzaWMoImhvdmVyIiwgZnVsbF93aWR0aCA9IFQpDQpgYGANCg0KDQojIyBDcmVhdGUgcGFyYW1ldGVycyBmaWxlDQoNClRoZSBwaXBlbGluZSBhbHNvIHJlcXVpcmVzIGEgbG9jdXMgcGFyYW1ldGVycyBmaWxlIHdoaWNoIGxpc3RzIGltcG9ydGFudCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdGFyZ2V0IGJhcmNvZGVzLCBhcyB3ZWxsIGFzIHRoZSBQSE1NIG1vZGVsIHVzZWQgdG8gY2xlYW4gdGhlIGxvY2ksIGFzIHdlbGwgYXMgdGhlIGRpZmZlcmVudCByZWZlcmVuY2UgZGF0YWJhc2VzIHRoYXQgYXJlIHRvIGJlIHVzZWQgZm9yIHRheG9ub21pYyBhc3NpZ25tZW50Lg0KDQpJZiBtdWx0aXBsZSByZWZlcmVuY2UgZGF0YWJhc2VzIGFyZSB0byBiZSB1c2VkIGl0ZXJhdGl2ZWx5IGZvciBhc3NpZ25tZW50LCB0aGV5IHNob3VsZCBiZSBzcGxpdCB3aXRoIGEgc2VtaWNvbG9uLCBhbmQgaW4gdGhlIG9yZGVyIHRoZXkgYXJlIHRvIGJlIHVzZWQgZm9yIGFzc2lnbm1lbnQuDQoNClRoZSBiZWxvdyBjb2RlIGdlbmVyYXRlcyB0aGUgbG9jaV9wYXJhbXMuY3N2IGZpbGUsIGhvd2V2ZXIgdGhpcyBjYW4gYWxzbyBiZSBkb25lIGluIGV4Y2VsLg0KDQoNCmBgYHtyIENyZWF0ZSBwYXJhbWV0ZXJzIGZpbGUsIGV2YWw9VFJVRX0NCnBhcmFtcyA8LSB0aWJibGUoDQogIHBjcl9wcmltZXJzID0gYygiZndoRjItZndoUjJuRGFjIiwgIkVJRjNMbWluaUY0LUVJRjNsbWluaVI0IiksDQogIHRhcmdldF9nZW5lPWMoIkNPSSIsICJFSUYzTCIpLA0KICBwaG1tID0gYygicmVmZXJlbmNlL3BobW0vQmFjdHJvY2VyYV9DT0kucmRzIiwgInJlZmVyZW5jZS9waG1tL0JhY3Ryb2NlcmFfRUlGM0wucmRzIiksDQogIHJlZl9kYiA9IGMoInJlZmVyZW5jZS9DT0lfaW50ZXJuYWxfaWR0YXhhLnJkcztyZWZlcmVuY2UvQ09JX2lkdGF4YS5yZHMiLCJyZWZlcmVuY2UvRUlGM0xfaW50ZXJuYWxfaWR0YXhhLnJkcztyZWZlcmVuY2UvRUlGM0xfaWR0YXhhLnJkcyIpLA0KICBibGFzdF9kYiA9IGMoInJlZmVyZW5jZS9DT0lfaW50ZXJuYWwuZmEuZ3o7cmVmZXJlbmNlL0NPSV9oaWVyYXJjaGlhbC5mYS5neiIsICJyZWZlcmVuY2UvRUlGM0xfaW50ZXJuYWwuZmEuZ3o7cmVmZXJlbmNlL0VJRjNMX2hpZXJhcmNoaWFsLmZhLmd6IiksDQogIGV4cF9sZW5ndGggPSBjKDIwNSwgMjE3KSwNCiAgZ2VuZXRpY19jb2RlID0gYygiU0dDNCIsICJTR0MwIiksDQogIGNvZGluZyA9IGMoVFJVRSwgVFJVRSkNCikNCg0KIyBXcml0ZSBvdXQgdGhlIHBhcmFtZXRlcnMgZmlsZQ0Kd3JpdGVfY3N2KHBhcmFtcywgInNhbXBsZV9kYXRhL2xvY2lfcGFyYW1zLmNzdiIpDQpgYGANCg0KVGhlIHJlc3VsdGluZyB0YWJsZSBzaG91bGQgbG9vayBsaWtlIHRoaXM6DQpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQ0KcGFyYW1zICU+JQ0KICBrYWJsZSgpICU+JQ0KICBjb2x1bW5fc3BlYygzLCB3aWR0aCA9ICIxY20iKSU+JQ0KICBjb2x1bW5fc3BlYyg0LCB3aWR0aCA9ICIxY20iKSU+JQ0KICBjb2x1bW5fc3BlYyg1LCB3aWR0aCA9ICIxY20iKSAlPiUNCiAga2FibGVfY2xhc3NpYygiaG92ZXIiLCBmdWxsX3dpZHRoID0gVCkNCmBgYA0KDQoNCiMgUnVuIHRoZSBwaXBlbGluZQ0KDQpOb3cgdGhhdCB0aGUgc2FtcGxlIGRhdGEgYW5kIHBhcmFtZXRlcnMgZmlsZSBpcyByZWFkeSwgaXRzIHRpbWUgdG8gcnVuIHRoZSBwaXBlbGluZSENCg0KIyMgVmlzdWFsaXNlIHRoZSBwaXBlbGluZSBzdGVwcw0KDQpgYGB7ciwgZXZhbD1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRX0NCiMgVmlzdWFsaXNlIHRoZSBwbGFubmVkIHRhcmdldHMgcGlwZWxpbmUNCnRhcl9nbGltcHNlKCkNCmBgYA0KDQoNCiMjIFJ1biB0aGUgdGFyZ2V0cyBwaXBlbGluZQ0KYGBge3J9DQp0YXJfbWFrZSgpDQpgYGANCg0KDQojIyBDaGVjayBxdWFsaXR5IGNvbnRyb2wgb3V0cHV0cw0KDQpUaGVyZSBhcmUgYSBmZXcgaW1wb3J0YW50IHF1YWxpdHkgY29udHJvbCBwbG90cyB0aGF0IGhhdmUgYmVlbiBhdXRvbWF0aWNhbGx5IGNyZWF0ZWQgdGhyb3VnaG91dCB0aGUgcGlwZWxpbmUuIFRoZXNlIHNob3VsZCBiZSBjaGVja2VkIHRvIGVuc3VyZSB0aGUgc2VxdWVuY2luZyBydW4gYW5kIGFuYWx5c2lzIGhhcyBydW4gc3VjY2Vzc2Z1bGx5Og0KDQoqIFNlcXVlbmNpbmcgcnVuIHF1YWxpdHkgY2hlY2sNCiAgKyBvdXRwdXQvbG9ncy9LNzdKUC9QRmNsdXN0ZXJzLnBkZg0KICArIG91dHB1dC9sb2dzL0s3N0pQL1FzY29yZV9MMS5wZGYNCiAgKyBvdXRwdXQvbG9ncy9LNzdKUC9hdmdfaW50ZW5zaXR5LnBkZg0KKiBTYW1wbGUgcXVhbGl0eSBjaGVjaw0KICArIG91dHB1dC9sb2dzL0s3N0pQL0ZBU1RRQy9uZ3NSZXBvcnRzX0Zhc3RxYy5odG1sDQoqIEluZGV4LXN3aXRjaGluZw0KICArIG91dHB1dC9sb2dzL0s3N0pQL2luZGV4X3N3aXRjaGluZy5wZGYNCiogRmlsdGVyaW5nIHBsb3RzDQogICsgb3V0cHV0L2xvZ3MvSzc3SlAvcHJlZmlsdF9xdWFscGxvdHMucGRmDQogICsgb3V0cHV0L2xvZ3MvSzc3SlAvcG9zdGZpbHRfcXVhbHBsb3RzLnBkZg0KKiBEQURBMiBlcnJvciBtb2RlbA0KICArIG91dHB1dC9sb2dzL0s3N0pQL2ZjaWRfZXJyb3Jtb2RlbC5wZGYNCiogUmVhZHMgc3Vydml2aW5nIHBpcGVsaW5lDQogICsgb3V0cHV0L2xvZ3MvSzc3SlAvcmVhZF9zdXJ2aXZhbC5wZGYgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+Tk9UIFdPUktJTkc8L3NwYW4+Lg0KDQoNCiMgQW5hbHlzZSB0aGUgcmVzdWx0cw0KDQpUaGUgb3V0cHV0cyBvZiB0aGUgcGlwZWxpbmUgYXJlIGFuIEFTViB0YWJsZSwgdGF4b25vbXkgdGFibGUsIGFuZCBzYW1wbGUgZGF0YSwgYXMgd2VsbCBhcyBhIHBoeWxvc2VxIG9iamVjdCBjb250YWluaW5nIHRoZXNlIDMgY29tcG9uZW50cy4gdGhlc2UgYXJlIGxvY2F0ZWQgaW4gdGhlIGZvbGxvd2luZyBkaXJlY3Rvcmllcw0KDQoqIG91dHB1dC9yZXN1bHRzL2ZpbmFsL3NlcXRhYi5jc3YNCiogb3V0cHV0L3Jlc3VsdHMvZmluYWwvdGF4dGFiLmNzdg0KKiBvdXRwdXQvcmVzdWx0cy9maW5hbC9zYW1kZi5jc3YNCiogb3V0cHV0L3Jkcy9wc19maWx0ZXJlZC5yZHMNCg0KDQojIyBSZWFkIGluIGRhdGENCg0KSGVyZSB3ZSByZWFkIGluIHRoZSBwaHlsb3NlcSBvYmplY3QgY29udGFpbmluZyB0aGUgQVNWIHRhYmxlLCB0YXhvbm9teSB0YWJsZSwgYW5kIHNhbXBsZSBkYXRhDQpgYGB7ciBwaHlsb3NlcSwgZXZhbD1UUlVFfQ0KcHMgPC0gcmVhZFJEUygib3V0cHV0L3Jkcy9wc19maWx0ZXJlZC5yZHMiKQ0KDQojIFR1cm4gdGhlIHBoeWxvc2VxIG9iamVjdCBpbnRvIGFuIFIgZGF0YSBmcmFtZSwgYW5kIGFwcGx5IDAuMDElIG1pbmltdW0gYWJ1bmRhbmNlIHRocmVzaG9sZA0Kc3VtbWFyeV9kYXQgPC0gcHMgJT4lDQogIHNwZWVkeXNlcTo6dGF4X2dsb20odGF4cmFuayA9ICJTcGVjaWVzIikgJT4lICMgTWVyZ2VzIGFsbCBBU1ZzIGFzc2lnbmVkIHRvIHRoZSBzYW1lIHNwZWNpZXMNCiAgc3BlZWR5c2VxOjpwc21lbHQoKSAgJT4lICMgVHJhbnNmb3JtcyB0byBkYXRhIGZyYW1lDQogIGZpbHRlcihBYnVuZGFuY2UgPiAwICkgICU+JQ0KICBkcGx5cjo6c2VsZWN0KE9UVSwgU2FtcGxlLCBBYnVuZGFuY2UscGNyX3ByaW1lcnMsIHNhbXBsZV9pZCwgc2FtcGxlX25hbWUsIGVudmlyb25tZW50LCBjb2xsZWN0aW9uX2xvY2F0aW9uLCBjb2xsZWN0aW9uX2RhdGUsIGZjaWQsIHJhbmtfbmFtZXMocHMpKSAlPiUNCiAgZ3JvdXBfYnkoc2FtcGxlX2lkLCBwY3JfcHJpbWVycykgJT4lDQogIG11dGF0ZV9hdCh2YXJzKEFidW5kYW5jZSksIH4gLiAvIHN1bSguKSApICU+JQ0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMWUtNCkgIyBSZW1vdmUgYWxsIHVuZGVyIDAuMDElIGFidW5kYW5jZQ0KDQpgYGANCg0KDQojIyBIZWF0bWFwcw0KYGBge3IgaGVhdG1hcCwgZXZhbD1UUlVFfQ0KZ2cuaGVhdG1hcCA8LSBzdW1tYXJ5X2RhdCU+JQ0KICBtdXRhdGUoc2FtcGxlX25hbWUgPSBmYWN0b3Ioc2FtcGxlX25hbWUsbGV2ZWxzPWMoIlRyYXAxIiwgIlRyYXA2IiwgIlRyYXA3IiwgIlRyYXAxOSIsICJUcmFwMjAiKSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9c2FtcGxlX25hbWUsIHk9U3BlY2llcywgZmlsbD1BYnVuZGFuY2UpKSArDQogICAgZ2VvbV90aWxlKCkgKw0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCwgbmEudmFsdWUgPSBOQSwgYWxwaGE9MC45KSArDQogICAgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHM9cmV2KSsNCiAgICBiYXNlX3RoZW1lKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsDQogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZT0iaXRhbGljIiksDQogICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKSsNCiAgICAgIGxhYnMoeD0iU2FtcGxlIiwNCiAgICAgICAgICAgeT0iVGF4b24iLA0KICAgICAgICAgICBmaWxsPSJSZWxhdGl2ZSBhYnVuZGFuY2UiKSArDQogICAgZmFjZXRfZ3JpZCh+cGNyX3ByaW1lcnMsIGRyb3A9VFJVRSkNCg0KZ2dwbG90bHkoZ2cuaGVhdG1hcCkNCmBgYA0KDQojIyBCYXJwbG90cw0KYGBge3IgYmFycGxvdCwgZXZhbD1UUlVFfQ0KZ2cuYmFycGxvdCA8LSBzdW1tYXJ5X2RhdCU+JQ0KICBtdXRhdGUoc2FtcGxlX25hbWUgPSBmYWN0b3Ioc2FtcGxlX25hbWUsbGV2ZWxzPWMoIlRyYXAxIiwgIlRyYXA2IiwgIlRyYXA3IiwgIlRyYXAxOSIsICJUcmFwMjAiKSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9c2FtcGxlX25hbWUsIHk9QWJ1bmRhbmNlLCBmaWxsPVNwZWNpZXMpKSArDQogICAgZ2VvbV9jb2woY29sb3VyPSJibGFjayIpICsNCiAgICAjc2NhbGVfZmlsbF92aXJpZGlzX2MobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBuYS52YWx1ZSA9IE5BLCBhbHBoYT0wLjkpICsNCiAgICAjc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHM9cmV2KSsNCiAgICBiYXNlX3RoZW1lKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJpdGFsaWMiKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkpKw0KICAgICAgbGFicyh4PSJTYW1wbGUiLA0KICAgICAgICAgICB5PSJUYXhvbiIsDQogICAgICAgICAgIGZpbGw9IlJlbGF0aXZlIGFidW5kYW5jZSIpICsNCiAgICBmYWNldF9ncmlkKH5wY3JfcHJpbWVycywgZHJvcD1UUlVFKQ0KDQpnZ3Bsb3RseShnZy5iYXJwbG90KQ0KYGBg